﻿/*----------------------------------------------------------*
 *                         Brick Class                     *
 *                                                          *
 *                  Created By: Matthew Hoyes               *
 *                                                          *
 *      This class will eventually serve as an Abstract     *
 *      Class which all other types/colors of bricks        *
 *      will inherit from, overloading the base methods.    *
 *----------------------------------------------------------*/

#region USING STATEMENTS

using System;

using Microsoft.Xna.Framework.Graphics;     // For Texture2D
using Microsoft.Xna.Framework;
using System.Collections.Generic;              // For Vector2

#endregion USING STATEMENTS

namespace BrickBreaker.Core
{
    #region ENUMS
    //
    public enum BrickType { RED, BLUE, GREEN, ORANGE, GREY }
    public enum BrickDensity { REGULAR, SOLID }           // Regular is breakable. Solid is not
    public enum BrickHitState { SOLID, MIN_CRACK, MED_CRACK, MAX_CRACK, DESTROY_ANIM };

    public enum VertexLabel_Brick { A = 0, B, C, D, NONE };
    //Used to detect which section of the box had collision
    public enum CollisionRegion
    {
        NONE, //No Box Collision Detected
        TOP_LEFT, TOP_MID, TOP_RIGHT, 
        MID_LEFT, MID_RIGHT, 
        BOTTOM_LEFT, BOTTOM_MID, BOTTOM_RIGHT
    };
    //
    #endregion ENUMS


    public class BBBrick : BBSingleSprite
    {
        #region DATA_MEMBERS
        //
        private BrickType type;             //V0--: Type of brick to draw on grid 
        
        private int hitCount;               //V0--: Total # of hits req. to break the brick
        private int hitCountMax;            //V0--: Set once in construction. Used in hitState transisition check
        private BrickDensity density;       //V0--: Is brick breakable, based on value of 'hitCountMax'
        private BrickHitState hitState;     //V0--: Texture to render, based on hitState. Update Texture when 'hitCount' changes 
        private int scoreValue;             //V0--: Points achievement on destruction of brick
        
        #region INTERNAL_TRACKING
        private List<Vector2> brickVertices;    //V0--: Track location of all Vertices that make up Brick Mesh
        private CollisionRegion collisionRegion;//V0--: Track which section of the brick detected collision, after 'Box Collision = TRUE'
        private bool hasRegisteredCollision;    //V0--: Use this to track ONLY one time ComplexCollision, Reset once reflection has happened
        #endregion INTERNAL_TRACKING
        //
        #endregion DATA_MEMBERS



        #region OBJECT_CONSTRUCTION
        /// <summary>
        /// Creates a new brick to be used.
        /// </summary>
        /// <param name="texture">The texture of the brick.</param>
        /* M001: Overloaded Constructor */
        //public BBBrick(BrickType type, int seed) : base(null, Vector2.Zero)
        public BBBrick(BrickType type, int hitCount, BrickDensity density, int scoreValue) : base(null, Vector2.Zero)
        {
            this.type = type;

            this.hitCount = hitCount; //AssignHitCount(seed);
            this.hitCountMax = this.HitCount;
            this.density = density; //AssignDensity();
            UpdateStateTransition();
            UpdateTexture();
            this.scoreValue = scoreValue; //AssignScoreValue(seed);
            base.size = new Vector2(base.texture.Width, base.texture.Height);

            //Instantiate brickVertices here, but Clear and populate in  later
            this.brickVertices = new List<Vector2>();
            this.collisionRegion = CollisionRegion.NONE;
            this.hasRegisteredCollision = false;
        }
        //
        #endregion OBJECT_CONSTRUCTION



        #region PROPERTIES
        //
        //Override Position Property, internalize CalculateVertices()
        public override Vector2 Position
        {
            get { return base.Position; }
            set 
            {
                base.Position = value;
                //Calculates the vertices that make up the bounds of the brick.
                CalculateVertices2(); //calculateVertices(); 
            }
        }
        
        //Read-Only
        public BrickHitState HitState
        {
            get { return this.hitState; }
            //set { this.hitState = value; }
        }

        //Read-Only
        public int HitCount
        {
            get { return this.hitCount; }
            ////set {}
        }

        //Read-Only
        public BrickDensity Density
        {
            get { return this.density; }
        }

        //Read-Only
        public BrickType Type
        {
            get { return this.type; }
            //set { this.type = value; }
        }

        //Read-Only
        public int ScoreValue
        {
            get { return this.scoreValue; }
            //set { this.scoreValue = value; }
        }

        //M0--: Should call and register ComplexCollision ONCE, until reflection has occured
        //this State is internally updated in: Intersects(rect), hasCollided(sprite), ComplexCollision(sprite)
        public bool HasRegisteredCollision
        {
            get { return this.hasRegisteredCollision; }
            //set { this.hasRegisteredCollision = value; }
        }
        //
        #endregion PROPERTIES



        #region PRIVATE METHODS
        //
        //Assign a randomized 'hitCount' value, that takes to destroy brick
        private void AssignHitCount(int seed)
        {
            Random rand = new Random(seed);

            switch (this.type)
            {
                case BrickType.RED:
                case BrickType.BLUE:
                    this.hitCount = rand.Next(1, 3);
                    break;
                case BrickType.GREEN:
                    this.hitCount = rand.Next(2, 4);
                    break;
                case BrickType.ORANGE:
                    this.hitCount = rand.Next(3, 5);
                    break;
                default:
                    this.hitCount = 0;
                    break;
            }
        }

        //'hitCountMax' used to determine density
        private void AssignDensity()
        {
            if (this.hitCountMax == 0)
            {
                this.density = BrickDensity.SOLID;
            }
            else { this.density = BrickDensity.REGULAR; }
        }

        private void UpdateStateTransition()
        {
            switch (this.hitCountMax)
            {
                case 0: //UN-BREAKABLE BLOCK
                    this.hitState = BrickHitState.SOLID;
                    break;
                #region BREAKABLE
                case 1:
                    #region 1-SHOT_TO_BREAK
                    if (this.HitCount == 1)
                    {
                        this.hitState = BrickHitState.SOLID;
                    }
                    else
                    {
                        this.hitState = BrickHitState.DESTROY_ANIM;
                    }
                    break; 
                    #endregion
                case 2:
                    #region 2-SHOTS_TO_BREAK
                    if (this.HitCount == 2)
                    {
                        this.hitState = BrickHitState.SOLID;
                    }
                    else if (this.HitCount == 1)
                    {
                        this.hitState = BrickHitState.MAX_CRACK;
                    }
                    else
                    {
                        this.hitState = BrickHitState.DESTROY_ANIM;
                    }
                    break; 
                    #endregion
                case 3:
                    #region 3-SHOTS_TO_BREAK
                    if (this.HitCount == 3)
                    {
                        this.hitState = BrickHitState.SOLID;
                    }
                    else if (this.HitCount == 2)
                    {
                        this.hitState = BrickHitState.MIN_CRACK;
                    }
                    else if (this.HitCount == 1)
                    {
                        this.hitState = BrickHitState.MAX_CRACK;
                    }
                    else
                    {
                        this.hitState = BrickHitState.DESTROY_ANIM;
                    }
                    break; 
                    #endregion
                case 4:
                    #region 4-SHOTS_TO_BREAK
                    if (this.HitCount == 4)
                    {
                        this.hitState = BrickHitState.SOLID;
                    }
                    else if (this.HitCount == 3)
                    {
                        this.hitState = BrickHitState.MIN_CRACK;
                    }
                    else if (this.HitCount == 2)
                    {
                        this.hitState = BrickHitState.MED_CRACK;
                    }
                    else if (this.HitCount == 1)
                    {
                        this.hitState = BrickHitState.MAX_CRACK;
                    }
                    else
                    {
                        this.hitState = BrickHitState.DESTROY_ANIM;
                    }
                    break; 
                    #endregion
                #endregion BREAKABLE
            }
        }

        //Update Brick Texture, based on 1)Brick->hitState and 2)Brick->type
        private void UpdateTexture()
        {
            switch (this.hitState)
            {
                case BrickHitState.SOLID:
                    #region SOLID_STATE_TEXTURE
                    switch (this.type)
                    {
                        case BrickType.RED:
                            base.texture = BBGlobalConfig.brick_red;
                            break;
                        case BrickType.BLUE:
                            base.texture = BBGlobalConfig.brick_blue;
                            break;
                        case BrickType.GREEN:
                            base.texture = BBGlobalConfig.brick_green;
                            break;
                        case BrickType.ORANGE:
                            base.texture = BBGlobalConfig.brick_orange;
                            break;
                        case BrickType.GREY:
                            base.texture = BBGlobalConfig.brick_grey;
                            break;
                    }
                    break; 
                    #endregion
                    
                case BrickHitState.MIN_CRACK:
                    #region MIN_CRACK_TEXTURE
                    switch (this.type)
                    {
                        case BrickType.RED:
                            base.texture = BBGlobalConfig.brick_red_cracked;
                            break;
                        case BrickType.BLUE:
                            base.texture = BBGlobalConfig.brick_blue_cracked;
                            break;
                        case BrickType.GREEN:
                            base.texture = BBGlobalConfig.brick_green_cracked;
                            break;
                        case BrickType.ORANGE:
                            base.texture = BBGlobalConfig.brick_orange_cracked;
                            break;
                        case BrickType.GREY:
                            base.texture = BBGlobalConfig.brick_grey;
                            break;
                    }
                    break; 
                    #endregion

                case BrickHitState.MED_CRACK:
                    #region MED_CRACK_TEXTURE
                    switch (this.type)
                    {
                        case BrickType.RED:
                            base.texture = BBGlobalConfig.brick_red;
                            break;
                        case BrickType.BLUE:
                            base.texture = BBGlobalConfig.brick_blue_cracked2;
                            break;
                        case BrickType.GREEN:
                            base.texture = BBGlobalConfig.brick_green_cracked2;
                            break;
                        case BrickType.ORANGE:
                            base.texture = BBGlobalConfig.brick_orange_cracked2;
                            break;
                        case BrickType.GREY:
                            base.texture = BBGlobalConfig.brick_grey;
                            break;
                    }
                    break; 
                    #endregion

                case BrickHitState.MAX_CRACK:
                    #region MAX_CRACK_TEXTURE
                    switch (this.type)
                    {
                        case BrickType.RED:
                            base.texture = BBGlobalConfig.brick_red;
                            break;
                        case BrickType.BLUE:
                            base.texture = BBGlobalConfig.brick_blue_cracked2;
                            break;
                        case BrickType.GREEN:
                            base.texture = BBGlobalConfig.brick_green_cracked3;
                            break;
                        case BrickType.ORANGE:
                            base.texture = BBGlobalConfig.brick_orange_cracked3;
                            break;
                        case BrickType.GREY:
                            base.texture = BBGlobalConfig.brick_grey;
                            break;
                    }
                    break; 
                    #endregion

                case BrickHitState.DESTROY_ANIM:
                    #region DESTROY_ANIM_TEXTURE
                    switch (this.type)
                    {
                        case BrickType.RED:
                            base.texture = BBGlobalConfig.brick_red;
                            break;
                        case BrickType.BLUE:
                            base.texture = BBGlobalConfig.brick_blue;
                            break;
                        case BrickType.GREEN:
                            base.texture = BBGlobalConfig.brick_green;
                            break;
                        case BrickType.ORANGE:
                            base.texture = BBGlobalConfig.brick_orange;
                            break;
                        case BrickType.GREY:
                            base.texture = BBGlobalConfig.brick_grey;
                            break;
                    }
                    break; 
                    #endregion
            }
        }

        //Assign score value for each brick, rewarded to player on successfully destroying the brick
        private void AssignScoreValue(int seed)
        {
            Random rand = new Random(seed);
            int score;

            #region GENERATE_RAND_SCORE
            switch (this.type)
            {
                case BrickType.RED:
                    score = rand.Next(10, 31);
                    break;
                case BrickType.BLUE:
                    score = rand.Next(35, 51);
                    break;
                case BrickType.GREEN:
                    score = rand.Next(55, 71);
                    break;
                case BrickType.ORANGE:
                    score = rand.Next(75, 101);
                    break;
                default:
                    score = 0;
                    break;
            } 
            #endregion

            #region ROUND_CLOSEST_INTERVAL_OF_FIVE(5)
            if ((score % 10) < 5)
            {
                score -= (score % 10); //Lower value
            }
            else if ((score % 10) > 5)
            {
                score += (10 - (score % 10)); //Higher value
            } 
            #endregion

            this.scoreValue = score;
        }

        public void MultiplyScoreValue(int multiplier)
        {
            this.scoreValue *= multiplier;
        }

        public void DivideScoreValue(int diviser)
        {
            this.scoreValue /= diviser;
        }

        //Decrement
        private void SubtractHit()
        {
            if (this.hitCount > 0) { --this.hitCount; }
        }
        

        /// <summary>
        /// M0--: Update when Collision happens
        /// Calc two(2) Paddle vertices, closest to a sprite (Ball)
        /// </summary>
        /// <param name="spriteCenter">The center of an object (A center point).</param>
        private List<Vector2> nearestTwoVertice2Sprite(Vector2 spriteCenter)
        {
            Vector2[] nearest = new Vector2[2];
            nearest[0] = new Vector2(0.0f, 0.0f);
            nearest[1] = new Vector2(0.0f, 0.0f);

            nearest[0] = (brickVertices[0]);
            //nearest[1] = nearest[0] = (brickVertices[0]);

            float temp2ndMag = 0.0f;
            Vector2 temp2ndVect = new Vector2();
            
            //Magnitude |AC| and |BC|
            float first = 0.0f, second = 0.0f;
            //Start from PointA
            first = second = Vector2.Distance(brickVertices[0], spriteCenter);

            for (int i = 0; i < brickVertices.Count; i++)
            {
                //Second Place
                if (Vector2.Distance(brickVertices[i], spriteCenter) < second)
                {
                    second = Vector2.Distance(brickVertices[i], spriteCenter);
                    nearest[1] = brickVertices[i];
                    //First Place
                    if (second < first)
                    {
                        //Promote Second to First
                        temp2ndMag = first;
                        first = second;
                        second = temp2ndMag;
                        //Demote First to Second
                        temp2ndVect = nearest[0];
                        nearest[0] = brickVertices[i];
                        nearest[1] = temp2ndVect;
                    }
                }
            }//for-loop

            List<Vector2> nearestPts = new List<Vector2>();

            nearestPts.Add(nearest[0]);
            nearestPts.Add(nearest[1]);

            return nearestPts;
        }

        /// <summary>
        /// Calculates the vertices that make up the bounds of the brick.
        /// Called inside 'calculateBrickVertices()' method
        /// </summary>
        private void calculateVertices()
        {
            // clear the list
            brickVertices.Clear();

            // calculate the first top left corner
            brickVertices.Add(new Vector2(base.position.X, base.position.Y));

            // calculate the top vertices
            for (int i = 0; i < 5; i++)
            {
                brickVertices.Add(new Vector2(brickVertices[i].X + 10, brickVertices[i].Y));
            }

            // calculate the right vertices
            for (int i = 0; i < 3; i++)
            {
                brickVertices.Add(new Vector2(brickVertices[brickVertices.Count - 1].X, brickVertices[brickVertices.Count - 1].Y + 10));
            }

            // calculate the bottom vertices 
            for (int i = 0; i < 5; i++)
            {
                brickVertices.Add(new Vector2(brickVertices[brickVertices.Count - 1].X - 10, brickVertices[brickVertices.Count - 1].Y));
            }

            // calculate the left vertices
            for (int i = 0; i < 2; i++)
            {
                brickVertices.Add(new Vector2(brickVertices[brickVertices.Count - 1].X, brickVertices[brickVertices.Count - 1].Y - 10));
            }

        }

        private void CalculateVertices2()
        {
            this.brickVertices.Clear();

            this.brickVertices.Add(new Vector2(base.position.X, base.position.Y));                              //Vertex A
            this.brickVertices.Add(new Vector2(base.position.X + base.size.X, base.position.Y));                //Vertex B
            this.brickVertices.Add(new Vector2(base.position.X + base.size.X, base.position.Y + base.size.Y));  //Vertex C
            this.brickVertices.Add(new Vector2(base.position.X, base.position.Y + base.size.Y));                //Vertex D
        }

        private void CalculateCollisionRegion(Vector2 spriteCenter)
        {
            #region TOP_REGION
            //CollisionRegion.TOP_LEFT
            if ((spriteCenter.X <= this.brickVertices[(int)VertexLabel_Brick.A].X) &&
                (spriteCenter.Y <= this.brickVertices[(int)VertexLabel_Brick.A].Y)
                )
            {
                this.collisionRegion = CollisionRegion.TOP_LEFT;
            }
            //CollisionRegion.TOP_MID
            else if ((spriteCenter.X > this.brickVertices[(int)VertexLabel_Brick.A].X) &&
                (spriteCenter.X < this.brickVertices[(int)VertexLabel_Brick.B].X) &&
                (spriteCenter.Y <= this.brickVertices[(int)VertexLabel_Brick.A].Y) &&
                (spriteCenter.Y <= this.brickVertices[(int)VertexLabel_Brick.B].Y)
                )
            {
                this.collisionRegion = CollisionRegion.TOP_MID;
            }
            //CollisionRegion.TOP_RIGHT
            else if ((spriteCenter.X >= this.brickVertices[(int)VertexLabel_Brick.B].X) &&
                    (spriteCenter.Y <= this.brickVertices[(int)VertexLabel_Brick.B].Y)
                    )
            {
                this.collisionRegion = CollisionRegion.TOP_RIGHT;
            } 
            #endregion

            #region MID_REGION
            //CollisionRegion.MID_LEFT
            if ((spriteCenter.X <= this.brickVertices[(int)VertexLabel_Brick.A].X) &&
                (spriteCenter.X <= this.brickVertices[(int)VertexLabel_Brick.D].X) &&
                (spriteCenter.Y > this.brickVertices[(int)VertexLabel_Brick.A].Y) &&
                (spriteCenter.Y < this.brickVertices[(int)VertexLabel_Brick.D].Y)
                )
            {
                this.collisionRegion = CollisionRegion.MID_LEFT;
            }
            //CollisionRegion.MID_RIGHT
            else if ((spriteCenter.X >= this.brickVertices[(int)VertexLabel_Brick.B].X) &&
                (spriteCenter.X >= this.brickVertices[(int)VertexLabel_Brick.C].X) &&
                (spriteCenter.Y > this.brickVertices[(int)VertexLabel_Brick.B].Y) &&
                (spriteCenter.Y < this.brickVertices[(int)VertexLabel_Brick.C].Y)
                )
            {
                this.collisionRegion = CollisionRegion.MID_RIGHT;
            }
            #endregion

            #region BOTTOM_REGION
            //CollisionRegion.BOTTOM_LEFT
            if ((spriteCenter.X <= this.brickVertices[(int)VertexLabel_Brick.D].X) &&
                (spriteCenter.Y >= this.brickVertices[(int)VertexLabel_Brick.D].Y)
                )
            {
                this.collisionRegion = CollisionRegion.BOTTOM_LEFT;
            }
            //CollisionRegion.BOTTOM_MID
            else if ((spriteCenter.X > this.brickVertices[(int)VertexLabel_Brick.D].X) &&
                (spriteCenter.X < this.brickVertices[(int)VertexLabel_Brick.C].X) &&
                (spriteCenter.Y >= this.brickVertices[(int)VertexLabel_Brick.D].Y) &&
                (spriteCenter.Y >= this.brickVertices[(int)VertexLabel_Brick.C].Y)
                )
            {
                this.collisionRegion = CollisionRegion.BOTTOM_MID;
            }
            //CollisionRegion.BOTTOM_RIGHT
            else if ((spriteCenter.X >= this.brickVertices[(int)VertexLabel_Brick.C].X) &&
                    (spriteCenter.Y >= this.brickVertices[(int)VertexLabel_Brick.C].Y)
                    )
            {
                this.collisionRegion = CollisionRegion.BOTTOM_RIGHT;
            }
            #endregion
        }

        #region CALC_REFLECTION
        
        #region TOP_REGION
        //-----------------------------------------------------------
        private Vector2 Reflection_TL(BBSingleSprite sprite)
        {
            //TODO: brickTL - Clamp reflection direction so that we don't have <-1,0>, <0,-1>,<1,0>,<0,1> situations
            Vector2 reflect = new Vector2();
            //Using y = mX + b, Slope(m) = 1
            float y_intercept = this.brickVertices[(int)VertexLabel_Brick.A].Y - this.brickVertices[(int)VertexLabel_Brick.A].X;
            float m = 1.0f;

            #region CALC_REFLECTION
            if (sprite.Center.Y >= ((m * sprite.Center.X) + y_intercept))
            {
                //reflect same direction as ML
                reflect = new Vector2(-1 * sprite.Direction.X, sprite.Direction.Y);
            }
            else if (sprite.Center.Y < ((m * sprite.Center.X) + y_intercept))
            {
                //reflect same direction as TM
                reflect = new Vector2(sprite.Direction.X, -1 * sprite.Direction.Y);
            } 
            #endregion

            #region CLAMP_REFLECTION
            //Clamp reflection (NW->NE) to avoid  <0,-1>
            if (reflect.X >= 0 && reflect.X < 0.2)
            {
                //reflect same direction as ML
                reflect = new Vector2(0.2f, -0.8f);
            }
            //Clamp reflection (NE->NW) to avoid <0,-1>
            else if ( reflect.X <=0 && reflect.X > -0.2)
            {
                //reflect same direction as ML
                reflect = new Vector2(-0.2f, -0.8f);
            }

            //Clamp reflection (SW->NW) to avoid <-1,0>
            else if (reflect.Y <= 0 && reflect.Y > -0.2)
            {
                reflect = new Vector2(-0.8f, -0.2f);
            }
            //Clamp reflection (NW->SW) to avoid <-1,0>
            else if (reflect.Y >= 0 && reflect.Y < 0.2)
            {
                reflect = new Vector2(-0.8f, 0.2f);
            } 
            #endregion
            //return (new Vector2(-1 * sprite.Direction.X, -1 * sprite.Direction.Y) );
            return reflect;
        }

        //-----------------------------------------------------------
        private Vector2 Reflection_TM(BBSingleSprite sprite)
        {
            //TODO: brickTM - Clamp reflection direction so that we don't have <-1,0>, <0,-1>,<1,0>,<0,1> situations
            Vector2 reflect = new Vector2(sprite.Direction.X, -1 * sprite.Direction.Y);

            #region CLAMP_REFLECTION
            //Clamp reflection (NW->NE) to avoid  <1,0>
            if ( (reflect.Y <= 0) && (reflect.Y > -0.2) && (reflect.X > 0) )
            {
                reflect = new Vector2(0.8f, -0.2f);
            }
            //Clamp reflection (NE->NW) to avoid  <-1,0>
            else if ((reflect.Y <= 0) && (reflect.Y > -0.2) && (reflect.X < 0))
            {
                reflect = new Vector2(-0.8f, -0.2f);
            }
            #endregion

            return reflect; 
        }

        //-----------------------------------------------------------
        private Vector2 Reflection_TR(BBSingleSprite sprite)
        {
            //TODO: brickTR - Clamp reflection direction so that we don't have <-1,0>, <0,-1>,<1,0>,<0,1> situations
            Vector2 reflect = new Vector2();
            //Using y = mX + b, Slope(m) = -1
            float y_intercept = this.brickVertices[(int)VertexLabel_Brick.B].Y - this.brickVertices[(int)VertexLabel_Brick.B].X;
            float m = -1.0f;

            #region CALC_REFLECTION
            if (sprite.Center.Y >= ( (m * sprite.Center.X) + y_intercept) )
            {
                reflect = new Vector2(-1 * sprite.Direction.X, sprite.Direction.Y);
            }
            else if (sprite.Center.Y < ((m * sprite.Center.X) + y_intercept))
            {
                reflect = new Vector2(sprite.Direction.X, -1 * sprite.Direction.Y);
            }
            #endregion

            #region CLAMP_REFLECTION
            //Clamp reflection (NW->NE) to avoid  <0,-1>
            if (reflect.X >= 0 && reflect.X < 0.2)
            {
                //reflect same direction as ML
                reflect = new Vector2(0.2f, -0.8f);
            }
            //Clamp reflection (NE->NW) to avoid <0,-1>
            else if (reflect.X <= 0 && reflect.X > -0.2)
            {
                //reflect same direction as ML
                reflect = new Vector2(-0.2f, -0.8f);
            }
            //Clamp reflection (SE->NE) to avoid <1,0>
            else if (reflect.Y <= 0 && reflect.Y > -0.2)
            {
                reflect = new Vector2(0.8f, -0.2f);
            }
            //Clamp reflection (NE->SE) to avoid <1,0>
            else if (reflect.Y >= 0 && reflect.Y < 0.2)
            {
                reflect = new Vector2(0.8f, 0.2f);
            }
            #endregion

            //return (new Vector2(-1 * sprite.Direction.X, -1 * sprite.Direction.Y) );
            return reflect;
        }
        #endregion

        #region MID_REGION
        //-----------------------------------------------------------
        private Vector2 Reflection_ML(BBSingleSprite sprite)
        {
            //TODO: brickML - Clamp reflection direction so that we don't have <-1,0>, <0,-1>,<1,0>,<0,1> situations
            Vector2 reflect = new Vector2(-1 * sprite.Direction.X, sprite.Direction.Y);

            #region CLAMP_REFLECTION
            //Clamp reflection (SW->NW) to avoid  <-1,0>
            if (reflect.Y <= 0 && reflect.Y > -0.2)
            {
                //reflect same direction as ML
                reflect = new Vector2(-0.8f, -0.2f);
            }
            //Clamp reflection (NW->SW) to avoid  <-1,0>
            else if (reflect.Y >= 0 && reflect.Y < 0.2)
            {
                //reflect same direction as ML
                reflect = new Vector2(-0.8f, 0.2f);
            }
            #endregion

            return reflect;
        }

        //-----------------------------------------------------------
        private Vector2 Reflection_MR(BBSingleSprite sprite)
        {
            //TODO: brickMR - Clamp reflection direction so that we don't have <-1,0>, <0,-1>,<1,0>,<0,1> situations
            Vector2 reflect = new Vector2(-1 * sprite.Direction.X, sprite.Direction.Y);

            #region CLAMP_REFLECTION
            //Clamp reflection (SE->NE) to avoid  <1,0>
            if (reflect.Y <= 0 && reflect.Y > -0.2)
            {
                //reflect same direction as ML
                reflect = new Vector2(0.8f, -0.2f);
            }
            //Clamp reflection (NE->SE) to avoid  <1,0>
            else if (reflect.Y >= 0 && reflect.Y < 0.2)
            {
                //reflect same direction as ML
                reflect = new Vector2(0.8f, 0.2f);
            }
            #endregion

            return reflect;
        } 
        #endregion

        #region BOTTOM_REGION
        //-----------------------------------------------------------
        private Vector2 Reflection_BL(BBSingleSprite sprite)
        {
            //TODO: brickBL - Clamp reflection direction so that we don't have <-1,0>, <0,-1>,<1,0>,<0,1> situations
            Vector2 reflect = new Vector2();
            //Using y = mX + b, Slope(m) = -1
            float y_intercept = this.brickVertices[(int)VertexLabel_Brick.D].Y - this.brickVertices[(int)VertexLabel_Brick.D].X;
            float m = -1.0f;

            #region CALC_REFLECTION
            if (sprite.Center.Y >= ((m * sprite.Center.X) + y_intercept))
            {
                reflect = new Vector2(-1 * sprite.Direction.X, sprite.Direction.Y);
            }
            else if (sprite.Center.Y < ((m * sprite.Center.X) + y_intercept))
            {
                reflect = new Vector2(-1 * sprite.Direction.X, sprite.Direction.Y);
            }
            #endregion

            #region CLAMP_REFLECTION
            //Clamp reflection (SW->SE) to avoid  <0,1>
            if (reflect.X >= 0 && reflect.X < 0.2)
            {
                //reflect same direction as ML
                reflect = new Vector2(0.2f, 0.8f);
            }
            //Clamp reflection (SE->SW) to avoid <0,1>
            else if (reflect.X <= 0 && reflect.X > -0.2)
            {
                //reflect same direction as ML
                reflect = new Vector2(-0.2f, 0.8f);
            }
            //Clamp reflection (SW->NW) to avoid <-1,0>
            else if (reflect.Y <= 0 && reflect.Y > -0.2)
            {
                reflect = new Vector2(-0.8f, -0.2f);
            }
            //Clamp reflection (NW->SW) to avoid <-1,0>
            else if (reflect.Y >= 0 && reflect.Y < 0.2)
            {
                reflect = new Vector2(-0.8f, 0.2f);
            }
            #endregion
            
            return reflect;
        }

        //-----------------------------------------------------------
        private Vector2 Reflection_BM(BBSingleSprite sprite)
        {
            //TODO: brickBM - Clamp reflection direction so that we don't have <-1,0>, <0,-1>,<1,0>,<0,1> situations
            Vector2 reflect = new Vector2(sprite.Direction.X, -1 * sprite.Direction.Y);

            #region CLAMP_REFLECTION
            //Clamp reflection (SW->SE) to avoid  <0,1>
            if ((reflect.X >= 0) && (reflect.X < 0.2) && (reflect.Y > 0))
            {
                reflect = new Vector2(0.2f, 0.8f);
            }
            //Clamp reflection (SE->SW) to avoid  <0,1>
            else if ((reflect.X <= 0) && (reflect.X > -0.2) && (reflect.Y > 0))
            {
                reflect = new Vector2(-0.2f, 0.8f);
            }
            #endregion

            return reflect; 
        }

        //-----------------------------------------------------------
        private Vector2 Reflection_BR(BBSingleSprite sprite)
        {
            //TODO: brickBR - Clamp reflection direction so that we don't have <-1,0>, <0,-1>,<1,0>,<0,1> situations
            Vector2 reflect = new Vector2();
            //Using y = mX + b, Slope(m) = 1
            float y_intercept = this.brickVertices[(int)VertexLabel_Brick.C].Y - this.brickVertices[(int)VertexLabel_Brick.C].X;
            float m = 1.0f;

            #region CALC_REFLECTION
            if (sprite.Center.Y >= ((m * sprite.Center.X) + y_intercept))
            {
                reflect = new Vector2(sprite.Direction.X, -1 * sprite.Direction.Y);
            }
            else if (sprite.Center.Y < ((m * sprite.Center.X) + y_intercept))
            {
                reflect = new Vector2(-1 * sprite.Direction.X, sprite.Direction.Y);
            }
            #endregion

            #region CLAMP_REFLECTION
            //Clamp reflection (SW->SE) to avoid  <0,1>
            if (reflect.X <= 0 && reflect.X > -0.2)
            {
                //reflect same direction as ML
                reflect = new Vector2(-0.2f, 0.8f);
            }
            //Clamp reflection (SE->SW) to avoid <0,1>
            else if (reflect.X >= 0 && reflect.X < 0.2)
            {
                //reflect same direction as ML
                reflect = new Vector2(0.2f, 0.8f);
            }
            //Clamp reflection (SE->NE) to avoid <1,0>
            else if (reflect.Y <= 0 && reflect.Y > -0.2)
            {
                reflect = new Vector2(0.8f, -0.2f);
            }
            //Clamp reflection (NE->SE) to avoid <1,0>
            else if (reflect.Y >= 0 && reflect.Y < 0.2)
            {
                reflect = new Vector2(0.8f, 0.2f);
            }
            #endregion
            
            return reflect;
        } 
        #endregion
        
        #endregion CALC_REFELCTION

        //
        #endregion PRIVATE METHODS



        #region PUBLIC METHODS

        public override bool Intersects(Rectangle rect)
        {
            //Reset hasRegisteredCollision = false once, Sprite(ball) has reflected
            if (!base.Intersects(rect))
            {
                this.hasRegisteredCollision = false;
                //TODO: Add Code - isBallTrappedInsideBrick 
                //this.isBallTrappedInsideBrick = false;
            }
            return base.Intersects(rect);
        }

        /// <summary>
        /// Update texture, based on hitState change
        /// </summary>
        public void Update()
        {
            UpdateStateTransition();//Update hitState
            UpdateTexture();        //Update texture
        }

        /// <summary>
        /// Draws the brick.  Call this in a spritebatch.Begin()/End() block.
        /// </summary>
        /// <param name="spriteBatch">Your spritebatch.  Make sure you have already called begin().</param>
        public override void Draw(SpriteBatch spriteBatch)
        {
            spriteBatch.Draw(texture, position, Color.White);
            //spriteBatch.DrawString(BBGlobalConfig.font, this.hitCount.ToString(), this.position, Color.White);
            spriteBatch.DrawString(BBGlobalConfig.font, this.scoreValue.ToString(), this.position, Color.White);
        }

        /// <summary>
        /// Move method should be called in an Update method. Serves to
        /// move the brick on screen.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Move(GameTime gameTime)
        {
            // do nothing
        }

        /// <summary>
        /// Resets the brick and all it's properties.
        /// </summary>
        public override void Reset()
        {
            this.hasRegisteredCollision = false; //Check ComplexCollision once, under ball reflects
            //TODO: Add Code - isBallTrappedInsideBrick 
            //this.isBallTrappedInsideBrick = false;
        }

        /// <summary>
        /// Checks whether the brick has collided with an object.
        /// Axis Aligned Bounding Box collision check.
        /// </summary>
        /// <param name="sprite">The object being checked for collision.</param>
        public bool hasCollided(BBSingleSprite sprite)
        {
            bool hasCollided = false;

            if (this.Intersects(sprite.getRectBounds()))
            {
                ////SubtractHit();
                ////hasCollided = true;
                CalculateCollisionRegion(sprite.Center);
                
                Vector2 temp, ball_MinVertex, ball_MaxVertex;

                switch (this.collisionRegion)
                {
                    case CollisionRegion.NONE:
                        hasCollided = false;
                        break;

                    case CollisionRegion.TOP_LEFT:
                        #region TOP_LEFT
                        //Find Linear Eqtn ball.Center & Vertex_A
                        //float slope = (sprite.Center.Y - this.brickVertices[(int)VertexLabel_Brick.A].Y) / (sprite.Center.X - this.brickVertices[(int)VertexLabel_Brick.A].X);
                        //float y_Intercept = sprite.Center.Y - (sprite.Center.X * slope);
                        temp = new Vector2(sprite.Center.X - this.brickVertices[(int)VertexLabel_Brick.A].X, sprite.Center.Y - this.brickVertices[(int)VertexLabel_Brick.A].Y);
                        temp.Normalize();

                        ball_MinVertex = sprite.Center;
                        ball_MaxVertex = sprite.Center + (temp * (sprite.Size / 2));

                        if (Vector2.Distance(ball_MinVertex, ball_MaxVertex) >=
                             Vector2.Distance(ball_MinVertex, this.brickVertices[(int)VertexLabel_Brick.A])
                            )
                        {
                            hasCollided = true;
                        }
                        break; 
                        #endregion

                    case CollisionRegion.TOP_MID:
                        #region TOP_MID
                        if (sprite.Size.Y / 2 >=
                            (int)Math.Abs(sprite.Center.Y - this.brickVertices[(int)VertexLabel_Brick.A].Y)
                            )
                        {
                            hasCollided = true;
                        }
                        break; 
                        #endregion

                    case CollisionRegion.TOP_RIGHT:
                        #region TOP_RIGHT
                        temp = new Vector2(sprite.Center.X - this.brickVertices[(int)VertexLabel_Brick.B].X, sprite.Center.Y - this.brickVertices[(int)VertexLabel_Brick.B].Y);
                        temp.Normalize();

                        ball_MinVertex = sprite.Center;
                        ball_MaxVertex = sprite.Center + (temp * (sprite.Size / 2));

                        if (Vector2.Distance(ball_MinVertex, ball_MaxVertex) >=
                             Vector2.Distance(ball_MinVertex, this.brickVertices[(int)VertexLabel_Brick.B])
                            )
                        {
                            hasCollided = true;
                        }
                        break; 
                        #endregion
                    
                    case CollisionRegion.MID_LEFT:
                        #region MID_LEFT
                        if (sprite.Size.X / 2 >=
                            (int)Math.Abs(sprite.Center.X - this.brickVertices[(int)VertexLabel_Brick.A].X)
                            )
                        {
                            hasCollided = true;
                        }
                        break;
                        #endregion

                    case CollisionRegion.MID_RIGHT:
                        #region MID_RIGHT
                        if (sprite.Size.X / 2 >=
                            (int)Math.Abs(sprite.Center.X - this.brickVertices[(int)VertexLabel_Brick.B].X)
                            )
                        {
                            hasCollided = true;
                        }
                        break;
                        #endregion

                    case CollisionRegion.BOTTOM_LEFT:
                        #region BOTTOM_LEFT
                        temp = new Vector2(sprite.Center.X - this.brickVertices[(int)VertexLabel_Brick.D].X, sprite.Center.Y - this.brickVertices[(int)VertexLabel_Brick.D].Y);
                        temp.Normalize();

                        ball_MinVertex = sprite.Center;
                        ball_MaxVertex = sprite.Center - (temp * (sprite.Size / 2));

                        if (Vector2.Distance(ball_MinVertex, ball_MaxVertex) >=
                             Vector2.Distance(ball_MinVertex, this.brickVertices[(int)VertexLabel_Brick.D])
                            )
                        {
                            hasCollided = true;
                        }
                        break;
                        #endregion

                    case CollisionRegion.BOTTOM_MID:
                        #region BOTTOM_MID
                        if (sprite.Size.Y / 2 >=
                            (int)Math.Abs(sprite.Center.Y - this.brickVertices[(int)VertexLabel_Brick.D].Y)
                            )
                        {
                            hasCollided = true;
                        }
                        break;
                        #endregion

                    case CollisionRegion.BOTTOM_RIGHT:
                        #region BOTTOM_RIGHT
                        temp = new Vector2(sprite.Center.X - this.brickVertices[(int)VertexLabel_Brick.C].X, sprite.Center.Y - this.brickVertices[(int)VertexLabel_Brick.C].Y);
                        temp.Normalize();

                        ball_MinVertex = sprite.Center;
                        ball_MaxVertex = sprite.Center - (temp * (sprite.Size / 2));

                        if (Vector2.Distance(ball_MinVertex, ball_MaxVertex) >=
                             Vector2.Distance(ball_MinVertex, this.brickVertices[(int)VertexLabel_Brick.C])
                            )
                        {
                            hasCollided = true;
                        }
                        break;
                        #endregion
                }//switch
            }

            //Only subtract hitCount, if collision has occured for sure
            if (hasCollided) { SubtractHit(); 
            }
            else {
                //Reset hasRegisteredCollision = false once, Sprite(ball) has reflected
                this.hasRegisteredCollision = false;
                //TODO: Add Code - isBallTrappedInsideBrick 
                //this.isBallTrappedInsideBrick = false;
            }

            return hasCollided;
        }

        /// <summary>
        /// Checks whether the brick has collided with an object.
        /// Axis Aligned Bounding Box collision check.
        /// </summary>
        /// <param name="projectile">The projectile being checked for collision.</param>
        public bool hasCollided(BBProjectile projectile)
        {
            bool hasCollided = false;

            if (this.Intersects(projectile.getRectBounds()))
            {
                hasCollided = true;
            }

            //Only subtract hitCount, if collision has occured for sure
            if (hasCollided)
            {
                SubtractHit();
            }
            else
            {
                //Reset hasRegisteredCollision = false once, Sprite(ball) has reflected
                this.hasRegisteredCollision = false;
            }

            return hasCollided;
        }


        /// <summary>
        /// Calculates the vertices that make up the bounds of the brick.
        /// </summary>
        ////public void calculateBrickVertices()
        ////{
        ////    calculateVertices();
        ////}

        /// <summary>
        /// Called when AABB collision checked passes. Calculates the reflection direction
        /// the ball shall travel once reflected off brick.
        /// </summary>
        /// <param name="sprite">The object being checked for collision.</param>
        public Vector2 ComplexCollision(BBSingleSprite sprite)
        {
            List<Vector2> nearestTwoPoints = nearestTwoVertice2Sprite(sprite.Center);

            //1. Find Vector_AB (delta_X, delta_Y), Slope_AB written in Vector2 format (X,Y) Not Scalar form (Rise/Run)
            Vector2 Vector_AB = (nearestTwoPoints[1] - nearestTwoPoints[0]);

            //-------------------------------------------------------------------
            //2. Find Normal(N)    _|_ of LineAB: N = (delta_Y, -delta_X)
            //N written in Vector2 format (X,Y) NOT in scalar: M2 = (1/(-M1)) OR M2 = (1/(-delta_X/delta_Y))
            //Normal in Vector2 = (Y, -X) from Slope_AB in #1
            Vector2 Normal = new Vector2(Vector_AB.Y, -Vector_AB.X);

            //-------------------------------------------------------------------
            //3. Find Normalize N1: [ (delta_Y / N), ((-ve delta_X) / N) ]
            float mag_N = (float)Math.Sqrt(Math.Pow(Normal.X, 2) + Math.Pow(Normal.Y, 2));

            Vector2 Normalize_N1 = new Vector2((Normal.X / mag_N), (Normal.Y / mag_N));

            //-------------------------------------------------------------------
            //4. Find Projection = (-sprite.Direction * Normalize_N1) * Normalize_N1

            //4A. First Find (|P|)magtitude of sprite.Direction onto Normal(N), 
            //i.e: Transfrom P in same direction as N: |P| = (-sprite.Direction * Normalize_N1)
            float mag_P = Vector2.Dot((-sprite.Direction), Normalize_N1);

            //-------------------------------------------------------------------
            //4B. Calc. Projection(P) = |P| * Normalize_N1
            Vector2 projection_P = Vector2.Multiply(Normalize_N1, mag_P);

            //-------------------------------------------------------------------
            //5. Find Reflection (R): R = 2 * P + sprite.Direction
            Vector2 reflection = new Vector2();
            reflection = (2 * projection_P + sprite.Direction);
            reflection.Normalize();
            //
            return reflection;
        }

        public Vector2 ComplexCollision2(BBSingleSprite sprite)
        {
            Vector2 reflection = new Vector2();

            #region COLLISION_CHECK
            switch (this.collisionRegion)
            {
                case CollisionRegion.TOP_LEFT:
                    reflection = Reflection_TL(sprite);
                    break;
                case CollisionRegion.TOP_MID:
                    reflection = Reflection_TM(sprite);
                    break;
                case CollisionRegion.TOP_RIGHT:
                    reflection = Reflection_TR(sprite);
                    break;
                case CollisionRegion.MID_LEFT:
                    reflection = Reflection_ML(sprite);
                    break;
                case CollisionRegion.MID_RIGHT:
                    reflection = Reflection_MR(sprite);
                    break;
                case CollisionRegion.BOTTOM_LEFT:
                    reflection = Reflection_BL(sprite);
                    break;
                case CollisionRegion.BOTTOM_MID:
                    reflection = Reflection_BM(sprite);
                    break;
                case CollisionRegion.BOTTOM_RIGHT:
                    reflection = Reflection_BR(sprite);
                    break;
            } 
            #endregion

            this.hasRegisteredCollision = true;
            return reflection;
        }

        #endregion PUBLIC METHODS
        
    }
}